/**
 *  Project Vorpal
 *  \file Common.h
 *  \author Russ Webb
 *
 *  \brief Contains useful functions and macros that are commonly used throughout the project
 *
 */

// The following documentation block makes the front page for the doxygen generated docs...
/**

 \mainpage Documents for Vorpal

 \section intro_sec About the Vorpal Language

 This language is used as an enel 428 class project. It is an embedable language written in C++.

 \section features Other Docs
 There is a latex manual in the docs folder. TODO: LINK from here?

 \section build_docs Generating the Documentation
 Ideally you are reading this as nice linked html in a browser and not from Common.h
 If you don't have the docs available they can be generated if you have doxygen installed.

 Generate the docs with the command <tt>./docs.sh</tt> or type doxygen in the Vorpal directory.

 \section install_sec Building

 To Build Vorpal type `./build` or use './simple-build.sh' tool. For more information type: `./build -h`


 \section development Development Choices

 \subsection howitworks How and why it does XXX



 \subsection majorComponents

 @author Russ Webb
 @author ENEL428 2008
 @author ENEL428 2009 (vorpal09@googlegroups.com )
*/


#ifndef COMMON_H
#define COMMON_H

typedef unsigned int uint;

#include <cstdlib>
#include <stdio.h>
#include <string.h>
#include <vector>
#include <map>
#include <set>
#include <deque>
#include <string>
#include <sstream>
#include <fstream>
#include <iostream>
#include <stdint.h>
#include "ErrorCodes.h"

using std::map;
using std::set;
using std::deque;
using std::pair;
using std::vector;
using std::string;
using std::ostringstream;
using std::istringstream;
using std::cout;
using std::endl;

/// A +1 incrementing loop covering the range [start, end). 
#define loop(var, start, end) for(int var = start, endV ## var = end; var < endV ## var; ++var)

namespace Vorpal{

	/// A hashed string
	typedef uint32_t Message;

	// kCellSize must be the full size of Cell, this is checked in AssertInstructionSanity
	#define kCellSize sizeof(float)

	typedef union{
		int32_t as_int;
		uint32_t as_uint;
		float as_float;
		Message as_message;
		struct{
			int16_t ip;
			int16_t op;
		} jump;
		int8_t as_bytes[kCellSize];
	} Cell;

}

uint32_t HashString(const char *str);
uint32_t HashBlock(const void *data, unsigned long size);

#ifndef useThreads
#define useThreads       true
#endif

#define traceAll         false
#define traceAllocation (false || traceAll)
#define traceOpcodes    (false || traceAll)
#define traceParser     (false || traceAll)
#define traceAsm        (false || traceAll)
#ifdef TRACE_PROCESS
#define traceProcess    TRACE_PROCESS
#else
#define traceProcess    (false || traceAll)
#endif

template < class T >
string ToString(const T &arg){
	ostringstream	out;
	out << arg;
	return(out.str());
}

#ifndef Assert

#ifndef DEBUG_MODE
#define DEBUG_MODE true
#endif

#if DEBUG_MODE

#define Confirm(cond) if(!(cond)) do_assert_failed(__FILE__, __LINE__, ERR_CONFIRM_FAILED)
#define Assert(cond) if(!(cond)) do_assert_failed(__FILE__, __LINE__, ERR_ASSERT_FAILED)
#define Require(cond) if(!(cond)) do_assert_failed(__FILE__, __LINE__, ERR_REQUIRE_FAILED)

#else

#define Confirm(cond)
#define Assert(cond)
#define Require(cond)

#endif

#endif

void do_assert_failed(const char *file, int line, int errCode);

#define Failure(errCode) do_Failure(errCode, __FILE__, __LINE__)

#define RuntimeAssert(cond, errCode) if(!(cond)) do_Failure(errCode, __FILE__, __LINE__)

inline void do_Failure(int errCode, const char *file, int line) {
	do_assert_failed(file, line, errCode);
}

/**  Class FileInMemory
 *
 *  Purpose: Loads the Vorpal file from memory
 *
 *  Public Methods:  Buffer: Returns a pointer to the file buffer
 *
 */

class FileInMemory{
public:
	FileInMemory(const char *filepath) : buffer(0){
		buffer = NewBufferFromFile(filepath);
	}

	~FileInMemory(){
		if(buffer){ delete[] buffer; }
	}
	
    /**
     *  Returns a pointer to the file buffer.
     * 
     *  the entire file is contained in the buffer as a string
     */
	const char *Buffer() const{ return buffer; }
private:
	char *buffer;

	static size_t FileSize(const char *filename){
		size_t result = 0;
		FILE *fp = fopen(filename, "r");
		if(fp){
			while(!feof(fp)){
				fgetc(fp);
				++result;
			}
			fclose(fp);
		}
		return result;
	}

	static char *NewBufferFromFile(const char* filename){
		int n = FileSize(filename);
		char *result = new char[n + 1];
		char *p = result;

		FILE *f = fopen(filename, "r");
		if(f == NULL){
			return NULL;
		}
		char c = fgetc(f);
		while(!feof(f)){
			*p++ = c;
			c = fgetc(f);
		}
		*p = '\0';
		fclose(f);
		return result;
	}
};

/// Used only for debugging, do not remove
class DestructorPrinter{
public:
	DestructorPrinter(int value = 0) : value(value){ }
	~DestructorPrinter(){ std::cout << value << std::endl; }
	int value;
};

// adapted from http://the-lazy-programmer.com/blog/?p=24
// usage: stack_array<CLASSNAME> array_name(STACK_NEW(CLASSNAME,size));
//	   or DeclareArray(array_name, CLASSNAME, size);

#define DeclareArray(name, type, size) stack_array<type> name(STACK_NEW(type,size))
#define STACK_NEW(type,size) new(static_cast<type*>(alloca(sizeof(type) * size))) type[size], size

template <class Type>
struct stack_array{
		stack_array(Type* p, size_t n) : ptr(p), num(n){}

		~stack_array(){
			for(size_t i = 0; i < num; ++i){ ptr[i].~Type(); }
		}

		operator Type*() { return ptr; }
private:
		Type *ptr;
		size_t num;
};

/**
 * Allow any lines of a file to printed out
 * useful for debugging purposes
 */
class FileLineReader {
public:
        FileLineReader(const char *filePath);

        void Print(uint lineNumber);
private:
        vector<string> lines;
};


#endif
